package cn.xinfei.xdecision.engine.runner.executor.handler;

import cn.xinfei.xdecision.common.model.component.guiderule.RuleInfo;
import cn.xinfei.xdecision.common.model.component.guiderule.vo.RuleBlockVo;
import cn.xinfei.xdecision.common.model.component.guiderule.vo.RuleVersionVo;
import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.model.datax.datamanage.Field;
import cn.xinfei.xdecision.common.service.component.guiderule.RuleConditionService;
import cn.xinfei.xdecision.common.service.component.scriptrule.RuleScriptVersionService;
import cn.xinfei.xdecision.common.service.datax.datamanage.FieldService;
import cn.xinfei.xdecision.common.utils.enums.enginex.ConditionValueTypeEnum;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.engine.runner.metadata.MetadataProvider;
import cn.xinfei.xdecision.engine.runner.service.RuleNodeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;


@Slf4j
@Service
public class RuleComponentHandler {

    @Autowired
    protected MetadataProvider metadataProvider;

    @Autowired
    @Qualifier("bizProcessorThreadPool")
    public ExecutorService bizProcessorThreadPool;

    @Autowired
    private NodeOutputHandler nodeOutputHandler;

    @Resource
    private FieldService fieldService;

    @Resource
    private RuleScriptVersionService ruleScriptVersionService;

    @Autowired
    private RuleConditionService conditionService;

    @Autowired
    private RuleNodeService ruleNodeService;

    public Map<String, FieldInfoVo> extractorInput(RuleInfo ruleInfo) {

        Map<String, FieldInfoVo> fieldVos = new HashMap<>();
        List<Long> versionIds = new ArrayList<>(); // 复杂规则集
        List<Long> scriptVersionIds = new ArrayList<>(); // 脚本规则集
        Long versionId = ruleInfo.getVersionId();
        Integer difficulty = ruleInfo.getDifficulty();
        if (difficulty != null && difficulty == 3) {
            scriptVersionIds.add(versionId); // 脚本式规则
        } else if (versionId != null) {
            versionIds.add(versionId); // 复杂规则
        }


        //获取字段en
        List<String> fieldEnList = new ArrayList<>();
        if (!versionIds.isEmpty()) {
            fieldEnList.addAll(conditionService.queryFieldEnByVersionIds(versionIds));
        }
        if (!scriptVersionIds.isEmpty()) {
            fieldEnList.addAll(ruleScriptVersionService.queryFieldEnByVersionIds(scriptVersionIds));
        }

        //筛选调那些循环或者嵌套中的字段
        fieldEnList = fieldEnList.stream().distinct().filter(f -> f != null && !f.contains(".") && !f.contains("%")).collect(Collectors.toList());
        if (fieldEnList != null && !fieldEnList.isEmpty()) {

            Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
            List<Field> fieldList = fieldService.selectFieldListByEns(fieldEnList);
            fieldList
                    .stream()
                    .filter(fc -> fieldInfo.containsKey(fc.getFieldCode()))
                    .collect(Collectors.toMap(Field::getFieldCode, v -> {
                        String fieldCode = v.getFieldCode();
                        FieldInfoVo value = fieldInfo.get(fieldCode);
                        FieldInfoVo ruleFieldInfoVo = new FieldInfoVo();
                        if (value != null) {
                            //复制一份的原因是，FieldInfoVo实例是从缓存中获取的，有可能是复用的，因此不可以赋值setRuleCode属性
                            BeanUtils.copyProperties(value, ruleFieldInfoVo);
                            ruleFieldInfoVo.setRuleCode(ruleInfo.getCode());
                            fieldVos.put(fieldCode, ruleFieldInfoVo);
                        }
                        return value;
                    }, (p1, p2) -> p1));
        }
        return fieldVos;
    }

    public Map<String, StrategyOutput> extractorOutput(List<RuleInfo> ruleInfoList) {

        Map<String, StrategyOutput> outputResult = new HashMap<>();

        for (int i = 0; i < ruleInfoList.size() && CollectionUtils.isNotEmpty(ruleInfoList); i++) {
            RuleInfo ruleInfo = ruleInfoList.get(i);
            RuleVersionVo version = ruleInfo.getVersion();
            if (null == version) {
//                        log.warn("当前组件没有版本实例,ruleInfo={}", JsonUtils.toJsonString(ruleInfo));
                continue;
            }
            List<RuleBlockVo> ruleBlockVoList = version.getRuleBlockVoList();
            if (ruleBlockVoList == null || ruleBlockVoList.size() == 0) {
//                        log.warn("当前组件没有block块实例,ruleBlockVoList={}", JsonUtils.toJsonString(ruleBlockVoList));
                continue;
            }

            Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
            for (int j = 0; j < ruleBlockVoList.size(); j++) {
                RuleBlockVo ruleBlockVo = ruleBlockVoList.get(j);

                //命中字段
                String resultFieldCode = ruleBlockVo.getResultFieldCode();
                FieldInfoVo fieldInfoVo = fieldInfo.get(resultFieldCode);
                if (null == fieldInfoVo) {
                    log.error("变量元数据信息不存在,resultFieldCode={}", resultFieldCode);
                    continue;
                }
                StrategyOutput output = new StrategyOutput();
                output.setFieldCode(resultFieldCode);
                output.setVarValueType(fieldInfoVo.getValueType());
                output.setValueType(ConditionValueTypeEnum.CONSTANT.getCode());
                outputResult.put(resultFieldCode, output);

                //得分字段
                String scoreFieldCode = ruleBlockVo.getScoreFieldCode();
                FieldInfoVo scoreField = fieldInfo.get(resultFieldCode);
                StrategyOutput scoreOut = new StrategyOutput();
                scoreOut.setFieldCode(scoreFieldCode);
                scoreOut.setVarValueType(scoreField.getValueType());
                scoreOut.setValueType(ConditionValueTypeEnum.CONSTANT.getCode());
                outputResult.put(scoreFieldCode, scoreOut);

                //命中输出
                List<StrategyOutput> strategyOutputList = ruleBlockVo.getStrategyOutputList();
                copy(ruleInfo, fieldInfo, strategyOutputList, outputResult);

                //未命中输出
                List<StrategyOutput> failOutputList = ruleBlockVo.getFailOutputList();
                copy(ruleInfo, fieldInfo, failOutputList, outputResult);
            }
        }

        return outputResult;
    }

    private void copy(RuleInfo ruleInfo, Map<String, FieldInfoVo> fieldInfo, List<StrategyOutput> from, Map<String, StrategyOutput> to) {
        if (CollectionUtils.isNotEmpty(from)) {
            from.forEach(out -> {
                FieldInfoVo fieldInfoVo = fieldInfo.get(out.getFieldCode());
                out.setVarValueType(fieldInfoVo.getValueType());
                out.setStage(fieldInfoVo.getStage());
                to.put(out.getFieldCode(), out);
            });
        }
    }
}
